package org.glorylight.hacks.social.controller;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import javax.imageio.ImageIO;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;

import org.apache.commons.io.IOUtils;
import org.glorylight.hacks.social.data.PictureData;
import org.glorylight.hacks.social.data.ScrapBookData;
import org.glorylight.hacks.social.data.UserData;
import org.glorylight.hacks.social.service.PictureService;
import org.glorylight.hacks.social.service.ScrapBookService;
import org.glorylight.hacks.social.service.UserService;
import org.glorylight.hacks.social.util.UUIDHelper;
import org.glorylight.hacks.social.xml.Galeries;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.sun.jersey.core.util.Base64;
import com.sun.jersey.multipart.BodyPartEntity;
import com.sun.jersey.multipart.MultiPart;

@Component
@Scope("prototype")
@Path("/picture")
public class PictureController extends AbstractBaseController {
	
	private static final Logger log = LoggerFactory.getLogger(PictureController.class);
	
	@Autowired
	private PictureService picService;
	
	@Autowired
	private UserService userService;
	
	@Autowired
	private ScrapBookService scrapService;
	
	/*
	 * Gallery
	 */
	
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	@Path("/galleries")
	public Galeries getGaleriesForUser() {
		User securityUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		log.info("username for lookup : {}", securityUser.getUsername());
		return populateGalery(picService.findAllByUser(securityUser.getUsername()));
	}

	private Galeries populateGalery(List<PictureData> result) {
		Galeries galeries = new Galeries();
		for (PictureData pd : result)
			galeries.getGaleries().add(pd);
		return galeries;
	}
	
	@POST
	@Consumes(MediaType.MULTIPART_FORM_DATA)
	@Produces(MediaType.APPLICATION_JSON)
	@Path("/gallery")
	public PictureData consumePicture(MultiPart multiPart) {
		UUID id = UUID.randomUUID();
		String userId = getActiveLogin();
		BodyPartEntity model = (BodyPartEntity) multiPart.getBodyParts().get(0).getEntity();
		try {
			BufferedImage bi = ImageIO.read(model.getInputStream());

			File file = new File("/var/uploaded/"+userId+"/gallery/" + UUIDHelper.uuidToString(id) + ".png");
			String picPath = "services/picture/gallery/"+UUIDHelper.uuidToString(id);
			if (file.isDirectory()) {
				ImageIO.write(bi, "png", file);
			} else {
				file.mkdirs();
				ImageIO.write(bi, "png", file);
			}
			PictureData pictureData = new PictureData(id, picPath, userId, "", "", new Date());
			picService.addPicture(pictureData);
			return pictureData;
		} catch (Exception e) {
			log.error("can not upload picture, {}",e.getMessage(), e);
			return null;
		}
	}
	
	@GET
	@Produces("image/png")
	@Path("/gallery/{pictureName}")
	public Response produceGalleryPicture(@PathParam("pictureName") String pictureName) {
		log.info("pictureName : {}", new Object[]{pictureName});
		String userId = getActiveLogin();
		File file = new File("/var/uploaded/" + userId + "/gallery/"+pictureName + ".png");
		ResponseBuilder response = Response.ok((Object) file);
		response.header("Content-Disposition", "attachment; filename=image_from_server.png");
		return response.build();
	}
	
	@POST
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Produces(MediaType.APPLICATION_JSON)
	@Path("/gallery/{pictureName}")
	public PictureData updatePictureTitle(@PathParam("pictureName") String pictureName, 
			@FormParam("title") String title, @FormParam("description") String description) {
		log.info("pictureName : {}", new Object[]{pictureName});
		String userId = getActiveLogin();
		UUID pictureId = UUIDHelper.stringToUUID(pictureName.toUpperCase().replace("-", ""));
		PictureData data = null;
		try {
			data = picService.updatePicture(new PictureData(pictureId, null, userId, title, description, null));
		} catch (Exception e) {
			log.error("could not update gallery",e);
		}
		return data;
	}
	
	@POST
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Path("/gallery/delete/{pictureName}")
	public Response removePicture(@PathParam("pictureName") String pictureName) {
		log.info("pictureName : {}", new Object[]{pictureName});
		UUID pictureId = UUIDHelper.stringToUUID(pictureName.toUpperCase().replace("-", ""));
		PictureData data = new PictureData(pictureId, null, null, null, null, null);
		try {
			picService.deletePicture(data);
		} catch (Exception e) {
			log.error("could not update gallery",e);
			return Response.status(Status.PRECONDITION_FAILED).build();
		}
		return Response.status(Status.ACCEPTED).build();
	}
	
	/*
	 * Scrap
	 */
	@POST
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Path("/scrap")
	public Response consumeScrap(InputStream image) {
		UUID id = UUID.randomUUID();
		String userId = getActiveLogin();
		boolean isProcessed = false;
		ScrapBookData bookData = null;
		try {
			StringWriter writer = new StringWriter();
			IOUtils.copy(image, writer);
			String data = writer.toString();
			String find = "base64%2C";
			String tokens = data.substring(data.indexOf(find) + find.length());
			String removeSpecialCharacter2F = StringUtils.replace(tokens, "%2F", "/");
			String removeSpecialCharacter2B = StringUtils.replace(removeSpecialCharacter2F, "%2B", "+");
			String removeSpecialCharacter3D = StringUtils.replace(removeSpecialCharacter2B, "%3D", "=");
			
			BufferedImage bi = ImageIO.read(new ByteArrayInputStream(Base64.decode(removeSpecialCharacter3D.getBytes())));
			if(bi == null)
				return Response.status(Response.Status.BAD_REQUEST)
						.entity("Failed to process attachments. Reason : " + "bi is null")
						.type(MediaType.TEXT_PLAIN).build();
			
			File file = new File("/var/uploaded/"+userId+"/scrap/" + UUIDHelper.uuidToString(id) + ".png");
			String picPath = "services/picture/scrap/"+UUIDHelper.uuidToString(id);
			if (file.isDirectory()) {
				ImageIO.write(bi, "png", file);
			} else {
				file.mkdirs();
				ImageIO.write(bi, "png", file);
			}
			isProcessed = true;
			
			//TODO: this query should be obsolete with the userid that will be pass from frontend
			UserData uData = userService.find(userId);
			bookData = new ScrapBookData(UUIDHelper.uuidToString(id), uData.getUserId(), new Date(), picPath, "", "", true);
			scrapService.addBook(bookData);
			
			log.debug("picture has been process");
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
		if (isProcessed) {
			return Response.status(Response.Status.OK)
					.entity(bookData)
					.type(MediaType.APPLICATION_JSON).build();
		}

		return Response.status(Response.Status.BAD_REQUEST)
				.entity(bookData)
				.type(MediaType.APPLICATION_JSON).build();
	}
	
	@GET
	@Produces("image/png")
	@Path("/scrap/{pictureName}")
	public Response productPicture(@PathParam("pictureName") String pictureName) {
		log.info("pictureName : {}", new Object[]{pictureName});
		String userId = getActiveLogin();
		File file = new File("/var/uploaded/" + userId + "/scrap/"+pictureName + ".png");
		ResponseBuilder response = Response.ok((Object) file);
		response.header("Content-Disposition", "attachment; filename=image_from_server.png");
		return response.build();
	}
	
	@POST
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Produces(MediaType.APPLICATION_JSON)
	@Path("/scrap/{pictureName}")
	public ScrapBookData updateScrapTitle(@PathParam("pictureName") String pictureName, 
			@FormParam("title") String title, @FormParam("description") String description, @FormParam("tag") String tag) {
		log.info("scrapname : {}, title :{}, tag: {}, description : {}", new Object[]{pictureName, title, description, tag});
		String userId = getActiveLogin();
		UUID pictureId = UUIDHelper.stringToUUID(pictureName.toUpperCase().replace("-", ""));
		ScrapBookData data = null;
		if(tag.charAt(0) != '#') {
			tag = "#".concat(tag);
		}
		tag = tag.toLowerCase().replace(" ", "_");
		try {
			ScrapBookData scrapBookData = new ScrapBookData(UUIDHelper.uuidToString(pictureId), userId, null, null, title, description, false);
			scrapBookData.setTag(tag);
			data = scrapService.updateBook(scrapBookData);
		} catch (Exception e) {
			log.error("could not update scrap",e);
		}
		return data;
	}
	
	@POST
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Produces(MediaType.APPLICATION_JSON)
	@Path("/scrap/delete/{pictureName}")
	public Response removeScrap(@PathParam("pictureName") String pictureName) {
		log.info("scrapname : {}", new Object[]{pictureName});
		UUID pictureId = UUIDHelper.stringToUUID(pictureName.toUpperCase().replace("-", ""));
		ScrapBookData book = new ScrapBookData(UUIDHelper.uuidToString(pictureId), null, null, null, null, null, false);
		try {
			scrapService.deleteBook(book);
		} catch (Exception e) {
			log.error("could not update scrap",e);
			return Response.status(Status.PRECONDITION_FAILED).build();
		}
		return Response.status(Status.ACCEPTED).build();
	}
}
